/*
 * 簡易ＰＳＧドライバー
 */
#pragma defseg(-C,psgdrv)
#pragma seg(psgdrv)
#pragma seg(dseg)
#pragma seg(bseg)

#include <system.h>
#include <v93.h>
#include <dev30.h>
#include <libm.h>
#include <intlib.h>
#include <common.h>
#include <soundbox.h>
#include <music.h>
#include <psgdrv.h>

#define	T120	(3728)		/* ダミー */
#define TRACK	(6)		/* ６トラック */
#define TONE	(128)		/* 音色数 */

#ifndef DEBUG
#define LOCAL   static
#else
#define LOCAL
#endif

extern unsigned char nNoteNumber[]; /* ノートナンバーテーブル */
extern unsigned char *cToneBuffer[TONE]; /* wave data */

LOCAL unsigned short  sStepCounter[TRACK]; /* ステップカウンタ */
LOCAL unsigned char   *cTrackBuffer[TRACK]; /* トラックバッファポインタ */
LOCAL unsigned char	*cTrackTop[TRACK]; /* トラックバッファ先頭アドレス */
LOCAL unsigned short	sChVolume[TRACK];  /* チャンネルボリューム */
LOCAL char            cTrackInfo[TRACK];  /* track info 0:off 1:on */

/*
 * 簡易ＰＳＧドライバー割り込みルーチン
 */
void interrupt
drvplay(void)
{
  regsave r1-r31;		/* レジスタ退避 */
  unsigned short ch;		/* レジスタ番号（HuC6230） */
  unsigned short value;		/* 書き込む値 */
  unsigned char	 cFrqLow;	/* 周波数粗調整 */
  unsigned char	 cFrqHigh;	/* 周波数微調整 */
  int	track;			/* track counter */
  /*  */
  for(track = 0; track < TRACK; track++){
    if(sStepCounter[track] != 0){
      sStepCounter[track]--;	/* カウンタが残ってるなら減らしてまつ */
    } else {			/* カウンタが残ってなければ動作開始 */
      __OUTPORTB(IO_SB_CHSEL,track);	/* チャンネルselect */
      while(sStepCounter[track] == 0) {	/* カウンタが増えるまで処理し続ける */
	switch(*cTrackBuffer[track]){	/* データによって分岐 */
	case    COUNT_WAIT:	/* 休み */
	  cTrackBuffer[track]++;
	  value = (short)*cTrackBuffer[track];
	  cTrackBuffer[track]++;
	  value |= (short)*cTrackBuffer[track] << 8;
	  sStepCounter[track] = value;	/* 読み込んだ値をカウンタにしまう */
	  break;
	case    NOTE_WRITE:	/* キーオン */
	  cTrackBuffer[track]++; 	/* テーブルから周波数を拾って */
	  value = (short)*cTrackBuffer[track];
	  value <<= 1;
	  cFrqHigh = nNoteNumber[value];
	  cFrqLow = nNoteNumber[value + 1];
	  __OUTPORTB(IO_SB_FRQL, cFrqLow); /* HuC6230に書き込む */
	  __OUTPORTB(IO_SB_FRQH, cFrqHigh);
	  value = 0x80 | sChVolume[track]; /* 発音状態にする */
	  __OUTPORTB(IO_SB_CHVOL, value);
	  break;
	case    NOTE_OFF:		/* キーオフ */
	  __OUTPORTB(IO_SB_CHVOL, 0x00); /* 無音状態にする */
	  break;
	case    CHLR_VOL:		/* チャンネルＬＲボリューム */
	  cTrackBuffer[track]++;
	  value = (short)*cTrackBuffer[track];
	  __OUTPORTB(IO_SB_PAN, value); /* HuC6230に書き込む */
	  break;
	case    CHVOL:		/* チャンネルボリューム */
	  cTrackBuffer[track]++;
	  value = (short)*cTrackBuffer[track];
	  sChVolume[track] = value & 0x1f; /* キーオン時用に保存する */
	  break;
	case    MAINVOL:	/* メインボリューム */
	  cTrackBuffer[track]++;
	  value = (short)*cTrackBuffer[track];
	  __OUTPORTB(IO_SB_MVOL, value); /* HuC6230に書き込む */
	  break;
	case    TEMPO:		/* テンポ（タイマーカウンタ） */
	  cTrackBuffer[track]++;		/* timer L */
	  value = (short)*cTrackBuffer[track];
	  cTrackBuffer[track]++;		/* timer H */
	  value |= (short)*cTrackBuffer[track] << 8;
	  _put_timer_tmr( value ); /* G/Aのダウンカウンタレジスタに書き込む */
	  break;
	case    GO_TOP:		/* 先頭に戻る */
	  cTrackBuffer[track] = cTrackTop[track]; /* ポインタを先頭に戻す */
	  break;
	case    END_OF_MUS:	/* 曲終端 */
	  cTrackInfo[track] = TRACK_OFF; /* 終了通知 */
	  cTrackBuffer[track] --;        /* カウンタ停止 */
	  sStepCounter[track] = 2;       /* ループを抜ける */
	  break;
	case	PROG_CHG:	/* 音色変更 */
	  cTrackBuffer[track]++;
	  waveset(track, cToneBuffer[*cTrackBuffer[track]]);
	  break;
	default:		/* どれでもなかった場合 */
	  if((*cTrackBuffer[track] & 0xf0) != REG_WRITE)
	    break;		/* レジスタ書き込みかどうか */
	  /* レジスタ直接操作 */
	  ch = *cTrackBuffer[track] & 0x0f; 	/* レジスタ番号取得 */
	  cTrackBuffer[track]++;
	  value = (short)*cTrackBuffer[track]; 	/* 書き込み値取得 */
	  ch <<= 1;
	  ch |= IO_SB_CHSEL;	/* I/Oポートアドレス作成 */
	  __OUTPORTB(ch,value);	/* 書き込み */
	  break;
	}
	cTrackBuffer[track]++;	/* ポインタ増加 */
      }
      sStepCounter[track]--;	/* カウンタを減らす */
    }
  }
  _put_timer_tcr(0x0003);	/* タイマーカウント再開 */
}
/*
 * 指定チャンネルに音色を設定する。
 */
void
waveset(unsigned char track, unsigned char *wave_data)
{
  int i;
  __OUTPORTB(IO_SB_CHSEL, track); /* チャンネル選択 */
  __OUTPORTB(IO_SB_CHVOL, 0x00); /* 音色設定モードにする */
  for(i = 0; i < 32; i++) {	/* 波形データ書き込み */
    __OUTPORTB(IO_SB_WAVE, *wave_data); 
    wave_data++;
  }
}
/*
 * ＰＳＧドライバー初期化
 */
void 
drvinit(void)
{
  int	i;
  /* メインボリューム０ */
  __OUTPORTB(IO_SB_MVOL, 0x00);
  /* 全チャンネルオフ ＆ ボリューム０ */
  for (i = 0; i < TRACK; i++){
    __OUTPORTB(IO_SB_CHSEL, i);	/* チャンネル０〜５選択 */
    __OUTPORTB(IO_SB_CHVOL, 0x00); /* チャネルボリューム０ */
    __OUTPORTB(IO_SB_NOISE, 0x00); /* ノイズオフ */
    __OUTPORTB(IO_SB_LFOFRQ, 0x01); /* lfo freq. */
    __OUTPORTB(IO_SB_LFOCR, 0x00); /* lfo off */
    sStepCounter[i] = 0;	/* ステップカウンターをクリア */
    cTrackInfo[i] = TRACK_OFF;	/* 停止状態 */
  }
  IrqSet(drvplay  , XINT_TIM);	/* 割り込みルーチン登録 */
  _put_timer_tmr( T120 );	/* 初期起動の為のダミーカウンタ設定 */
  _put_timer_tcr(0x0001);	/* TIEN = 1, TCE = 0, TIREQ = 0 */
}
/*
 * 演奏開始
 */
void
playstart(void)
{
  int track;
  /*  */
  for(track = 0; track < TRACK; track++){
    cTrackBuffer[track] = cTrackTop[track]; /* バッファポインタを曲の先頭に */
    cTrackInfo[track] = TRACK_ON; /* 演奏状態 */
    sStepCounter[track] = 0;	/* ステップカウンターをクリア */
  }
  _put_timer_tcr(0x0001);	/* タイマー設定 */
  _put_timer_tcr(0x0003);
  IrqOn(XINT_TIM);		/* 割り込み許可 */
}
/*
 * 演奏停止
 */
void
playstop(void)
{
  drvinit();			/* ドライバ初期化 */
  _put_timer_tcr(0x0000);	/* タイマー停止 */
  IrqOff(XINT_TIM);		/* 割り込みルーチン停止 */
}
/*
 * 曲データ登録
 */
void
dataset(unsigned char *cMusicBuffer[])
{
  cTrackTop[0] = cMusicBuffer[0];	/* 内部変数に代入 */
  cTrackTop[1] = cMusicBuffer[1];	/* 内部変数に代入 */
  cTrackTop[2] = cMusicBuffer[2];	/* 内部変数に代入 */
  cTrackTop[3] = cMusicBuffer[3];	/* 内部変数に代入 */
  cTrackTop[4] = cMusicBuffer[4];	/* 内部変数に代入 */
  cTrackTop[5] = cMusicBuffer[5];	/* 内部変数に代入 */
}
/*
 * 演奏状態を得る
 * 返り値：	０     全トラック停止
 *		１以上 いずれかのトラックが演奏中
 *		（bit 0〜5 までがトラック 0〜5 までの演奏状態
 *		  ０は停止中、１は演奏中）
 */
char
gettrackinfo( void )
{
  char  alltrack;
  int   i;
  /*  */
  alltrack = 0;			/* 初期化 */
  for(i = 0; i < TRACK; i++){
    alltrack |= cTrackInfo[i] << i; /* 各トラックの演奏状態計算 */
  }
  return alltrack;		/* 値を持って返る */
}
